/*
 * con_command.h
 *
 *  Created on: Sep 20, 2010
 *      Author: khanton
 */

#ifndef CON_COMMAND_H_
#define CON_COMMAND_H_

#include <boost/noncopyable.hpp>
#include <boost/asio.hpp>
#include "con_state.h"
#include "con_connection.h"
#include "sqlhandle.h"
#include "program.h"

// all packet must be multiple of 8
#define OUT_PACKET_HELLO_SIZE		8
#define OUT_PACKET_GETSTATUS_SIZE	8
#define OUT_PACKET_SETSTATUS_SIZE	8
#define OUT_PACKET_SYNCTIME_SIZE	16
#define OUT_PACKET_PROG				248
#define OUT_PROG_CHUNK				240

const unsigned char PSTART = 0x10;
const unsigned char SIGN_GET_STATUS = 0x20;
const unsigned char CONTROLLER_GET_STATUS = 0x20;
const unsigned char CONTROLLER_SYNC_TIME = 0x10; 		// 16
const unsigned char CONTROLLER_GSM_TU = 0x17;	 		// 23
const unsigned char CONTROLLER_START_PROG_NUM = 0x12;   // 18
const unsigned char CONTROLLER_WRITE_PROG = 0x11;	// 17


const unsigned char COMMAND_ACCEPTED = 0x0;
const unsigned char COMMAND_NOT_ACCEPTED = 0xff;

class controllers_network;

class con_command:
	private boost::noncopyable
{
	public:
		con_command(controllers_network &_network, sqlhandle &_sqlhandle);
		virtual ~con_command();

		enum {
			max_controller_packet_size = 240
		};

		typedef enum
		{
			start_write,
			start_last_write,
			start_timer
		} return_t;

		virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection) = 0;

		virtual bool need_read() = 0;

		virtual con_command *get_next_command() = 0;			// construct next command after write before read

		virtual return_t process_request(boost::asio::const_buffer _in_buffer, size_t _bytes_transferred, std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection) = 0;
		
		static std::string dump_packet(const unsigned char *_src, unsigned int _src_len);

	protected:

		static unsigned char calc_cs(const unsigned char *_buffer, size_t _len);
		static void build_packet(unsigned char *_dst, const unsigned char *_src, size_t _len, size_t _buff_len);
		static bool parse_packet(const unsigned char *_src, unsigned int _src_len,  const unsigned char **_dst, unsigned int &_dst_len);
		
		controllers_network &network_;
		sqlhandle &sqlhandle_;
};

class hello_command:
		public con_command
{
	public:
		hello_command(controllers_network &_network, sqlhandle &_sqlhandle);
		virtual ~hello_command();

		virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection );

		virtual bool need_read();

		virtual con_command *get_next_command();

		virtual return_t process_request(boost::asio::const_buffer _in_buffer, size_t _bytes_transferred, std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

	protected:

		bool pult_;
		con_state::controller_id controller_id_;

		boost::array<unsigned char, OUT_PACKET_HELLO_SIZE> answer_;
};

class get_status_command:
		public con_command
{
	public:
		explicit get_status_command(controllers_network &_network, sqlhandle &_sqlhandle, con_state::controller_id controller_id);

		virtual ~get_status_command();

		virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

		virtual bool need_read();

		virtual con_command *get_next_command();

		virtual return_t process_request(boost::asio::const_buffer _in_buffer, size_t _bytes_transferred, std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

	protected:

		boost::array<unsigned char, OUT_PACKET_GETSTATUS_SIZE> request_;
		con_state::controller_id controller_id_;
		bool need_sync_time_;
};

class sync_time_command:
		public get_status_command
{
public:
	explicit sync_time_command(controllers_network &_network, sqlhandle &_sqlhandle, con_state::controller_id controller_id);

	virtual ~sync_time_command();

	virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

	con_command *get_next_command();

protected:

	boost::array<unsigned char, OUT_PACKET_SYNCTIME_SIZE> request_;
};

class set_status_command:
		public get_status_command
{
	public:
		explicit set_status_command(controllers_network &_network, sqlhandle &_sqlhandle, con_state::controller_id _controller_id, unsigned char _status);

		virtual ~set_status_command();

		virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

		virtual con_command *get_next_command();

	protected:

		unsigned char status_;

		boost::array<unsigned char, OUT_PACKET_GETSTATUS_SIZE> request_;
};

class set_program_command:
		public get_status_command
{
	public:
		explicit set_program_command(controllers_network &_network, sqlhandle &_sqlhandle, con_state::controller_id _controller_id, unsigned char _program);

		virtual ~set_program_command();

		virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

		virtual con_command *get_next_command();

	protected:

		unsigned char program_;

		boost::array<unsigned char, OUT_PACKET_GETSTATUS_SIZE> request_;
};

class pult_command:
		public con_command
{
	public:
		explicit pult_command(controllers_network &_network, sqlhandle &_sqlhandle);

		virtual ~pult_command();

		virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

		virtual bool need_read();

		virtual con_command *get_next_command();

		virtual return_t process_request(boost::asio::const_buffer _in_buffer, size_t _bytes_transferred, std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

	protected:

		boost::array<unsigned char, 31> answer_status_;
		boost::array<unsigned char, 5> answer_tu_;
};

class con_load_command:
	public con_command
{
	public:
		con_load_command(controllers_network &_network, sqlhandle &_sqlhandle, con_state::controller_id _controller_id);
		virtual ~con_load_command();

		bool init_prog();		// true if can run program

		virtual bool create_command(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

		virtual bool need_read();

		virtual con_command *get_next_command();			// construct next command after write before read

		virtual return_t process_request(boost::asio::const_buffer _in_buffer, size_t _bytes_transferred, std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection);

	protected:
		bool build_next_packet(std::vector<boost::asio::const_buffer> &_out_buffers, con_connection_ptr _connection, bool _repeat = false);

		boost::array<unsigned char, OUT_PACKET_PROG> answer_prog_;

		program prog_;
		
		int repeat_count_;

		bool has_error_;
		bool stop_load_;

		con_state::controller_id controller_id_;

		typedef struct
		{
			unsigned char id_;
			unsigned short int offs_;
			char data_[OUT_PROG_CHUNK];
		} __attribute__((__packed__)) program_chunk_struct_t ;

};

#endif /* CON_COMMAND_H_ */
